{
  "cells": [
    {
      "cell_type": "raw",
      "id": "8f21bf6b",
      "metadata": {
        "vscode": {
          "languageId": "raw"
        }
      },
      "source": [
        "---\n",
        "keywords: [create_react_agent, create_react_agent()]\n",
        "---"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "579c24a2",
      "metadata": {},
      "source": [
        "# How to migrate from legacy LangChain agents to LangGraph\n",
        "\n",
        ":::info Prerequisites\n",
        "\n",
        "This guide assumes familiarity with the following concepts:\n",
        "- [Agents](/docs/concepts/agents)\n",
        "- [LangGraph.js](https://langchain-ai.github.io/langgraphjs/)\n",
        "- [Tool calling](/docs/how_to/tool_calling/)\n",
        "\n",
        ":::\n",
        "\n",
        "Here we focus on how to move from legacy LangChain agents to more flexible [LangGraph](https://langchain-ai.github.io/langgraphjs/) agents.\n",
        "LangChain agents (the\n",
        "[`AgentExecutor`](https://api.js.langchain.com/classes/langchain.agents.AgentExecutor.html)\n",
        "in particular) have multiple configuration parameters. In this notebook we will\n",
        "show how those parameters map to the LangGraph\n",
        "react agent executor using the [create_react_agent](https://langchain-ai.github.io/langgraphjs/reference/functions/prebuilt.createReactAgent.html) prebuilt helper method.\n",
        "\n",
        "For more information on how to build agentic workflows in LangGraph, check out\n",
        "the [docs here](https://langchain-ai.github.io/langgraphjs/how-tos/).\n",
        "\n",
        "#### Prerequisites\n",
        "\n",
        "This how-to guide uses OpenAI's `\"gpt-4o-mini\"` as the LLM. If you are running this guide as a notebook, set your OpenAI API key as shown below:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "24ef582f",
      "metadata": {
        "lines_to_next_cell": 2
      },
      "outputs": [],
      "source": [
        "// process.env.OPENAI_API_KEY = \"...\";\n",
        "\n",
        "// Optional, add tracing in LangSmith\n",
        "// process.env.LANGSMITH_API_KEY = \"ls...\";\n",
        "// process.env.LANGCHAIN_CALLBACKS_BACKGROUND = \"true\";\n",
        "// process.env.LANGSMITH_TRACING = \"true\";\n",
        "// process.env.LANGSMITH_PROJECT = \"How to migrate: LangGraphJS\";\n",
        "\n",
        "// Reduce tracing latency if you are not in a serverless environment\n",
        "// process.env.LANGCHAIN_CALLBACKS_BACKGROUND = \"true\";"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c1ff5c79",
      "metadata": {},
      "source": [
        "## Basic Usage\n",
        "\n",
        "For basic creation and usage of a tool-calling ReAct-style agent, the\n",
        "functionality is the same. First, let's define a model and tool(s), then we'll\n",
        "use those to create an agent.\n",
        "\n",
        ":::note\n",
        "The `tool` function is available in `@langchain/core` version 0.2.7 and above.\n",
        "\n",
        "If you are on an older version of core, you should use instantiate and use [`DynamicStructuredTool`](https://api.js.langchain.com/classes/langchain_core.tools.DynamicStructuredTool.html) instead.\n",
        ":::"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "1222c5e2",
      "metadata": {
        "lines_to_next_cell": 2
      },
      "outputs": [],
      "source": [
        "import { tool } from \"@langchain/core/tools\";\n",
        "import { z } from \"zod\";\n",
        "import { ChatOpenAI } from \"@langchain/openai\";\n",
        "\n",
        "const llm = new ChatOpenAI({\n",
        "  model: \"gpt-4o-mini\",\n",
        "});\n",
        "\n",
        "const magicTool = tool(async ({ input }: { input: number }) => {\n",
        "  return `${input + 2}`;\n",
        "}, {\n",
        "  name: \"magic_function\",\n",
        "  description: \"Applies a magic function to an input.\",\n",
        "  schema: z.object({\n",
        "    input: z.number(),\n",
        "  }),\n",
        "});\n",
        "\n",
        "const tools = [magicTool];\n",
        "\n",
        "const query = \"what is the value of magic_function(3)?\";"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "768d9e8c",
      "metadata": {},
      "source": [
        "For the LangChain\n",
        "[`AgentExecutor`](https://api.js.langchain.com/classes/langchain_agents.AgentExecutor.html),\n",
        "we define a prompt with a placeholder for the agent's scratchpad. The agent can\n",
        "be invoked as follows:\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "e52bf891",
      "metadata": {
        "lines_to_next_cell": 2
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{\n",
              "  input: \u001b[32m\"what is the value of magic_function(3)?\"\u001b[39m,\n",
              "  output: \u001b[32m\"The value of `magic_function(3)` is 5.\"\u001b[39m\n",
              "}"
            ]
          },
          "execution_count": 3,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "import {\n",
        "  ChatPromptTemplate,\n",
        "} from \"@langchain/core/prompts\";\n",
        "import { createToolCallingAgent } from \"langchain/agents\";\n",
        "import { AgentExecutor } from \"langchain/agents\";\n",
        "\n",
        "const prompt = ChatPromptTemplate.fromMessages([\n",
        "  [\"system\", \"You are a helpful assistant\"],\n",
        "  [\"placeholder\", \"{chat_history}\"],\n",
        "  [\"human\", \"{input}\"],\n",
        "  [\"placeholder\", \"{agent_scratchpad}\"],\n",
        "]);\n",
        "\n",
        "const agent = createToolCallingAgent({\n",
        "  llm,\n",
        "  tools,\n",
        "  prompt\n",
        "});\n",
        "const agentExecutor = new AgentExecutor({\n",
        "  agent,\n",
        "  tools,\n",
        "});\n",
        "\n",
        "await agentExecutor.invoke({ input: query });"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ba3e5db9",
      "metadata": {},
      "source": [
        "LangGraph's off-the-shelf\n",
        "[react agent executor](https://langchain-ai.github.io/langgraphjs/reference/functions/prebuilt.createReactAgent.html)\n",
        "manages a state that is defined by a list of messages. In a similar way to the `AgentExecutor`, it will continue to\n",
        "process the list until there are no tool calls in the agent's output. To kick it\n",
        "off, we input a list of messages. The output will contain the entire state of\n",
        "the graph - in this case, the conversation history and messages representing intermediate tool calls:\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "dcda7082",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "{\n",
            "  messages: [\n",
            "    HumanMessage {\n",
            "      \"id\": \"eeef343c-80d1-4ccb-86af-c109343689cd\",\n",
            "      \"content\": \"what is the value of magic_function(3)?\",\n",
            "      \"additional_kwargs\": {},\n",
            "      \"response_metadata\": {}\n",
            "    },\n",
            "    AIMessage {\n",
            "      \"id\": \"chatcmpl-A7exs2uRqEipaZ7MtRbXnqu0vT0Da\",\n",
            "      \"content\": \"\",\n",
            "      \"additional_kwargs\": {\n",
            "        \"tool_calls\": [\n",
            "          {\n",
            "            \"id\": \"call_MtwWLn000BQHeSYQKsbxYNR0\",\n",
            "            \"type\": \"function\",\n",
            "            \"function\": \"[Object]\"\n",
            "          }\n",
            "        ]\n",
            "      },\n",
            "      \"response_metadata\": {\n",
            "        \"tokenUsage\": {\n",
            "          \"completionTokens\": 14,\n",
            "          \"promptTokens\": 55,\n",
            "          \"totalTokens\": 69\n",
            "        },\n",
            "        \"finish_reason\": \"tool_calls\",\n",
            "        \"system_fingerprint\": \"fp_483d39d857\"\n",
            "      },\n",
            "      \"tool_calls\": [\n",
            "        {\n",
            "          \"name\": \"magic_function\",\n",
            "          \"args\": {\n",
            "            \"input\": 3\n",
            "          },\n",
            "          \"type\": \"tool_call\",\n",
            "          \"id\": \"call_MtwWLn000BQHeSYQKsbxYNR0\"\n",
            "        }\n",
            "      ],\n",
            "      \"invalid_tool_calls\": [],\n",
            "      \"usage_metadata\": {\n",
            "        \"input_tokens\": 55,\n",
            "        \"output_tokens\": 14,\n",
            "        \"total_tokens\": 69\n",
            "      }\n",
            "    },\n",
            "    ToolMessage {\n",
            "      \"id\": \"1001bf20-7cde-4f8b-81f1-1faa654a8bb4\",\n",
            "      \"content\": \"5\",\n",
            "      \"name\": \"magic_function\",\n",
            "      \"additional_kwargs\": {},\n",
            "      \"response_metadata\": {},\n",
            "      \"tool_call_id\": \"call_MtwWLn000BQHeSYQKsbxYNR0\"\n",
            "    },\n",
            "    AIMessage {\n",
            "      \"id\": \"chatcmpl-A7exsTk3ilzGzC8DuY8GpnKOaGdvx\",\n",
            "      \"content\": \"The value of `magic_function(3)` is 5.\",\n",
            "      \"additional_kwargs\": {},\n",
            "      \"response_metadata\": {\n",
            "        \"tokenUsage\": {\n",
            "          \"completionTokens\": 14,\n",
            "          \"promptTokens\": 78,\n",
            "          \"totalTokens\": 92\n",
            "        },\n",
            "        \"finish_reason\": \"stop\",\n",
            "        \"system_fingerprint\": \"fp_54e2f484be\"\n",
            "      },\n",
            "      \"tool_calls\": [],\n",
            "      \"invalid_tool_calls\": [],\n",
            "      \"usage_metadata\": {\n",
            "        \"input_tokens\": 78,\n",
            "        \"output_tokens\": 14,\n",
            "        \"total_tokens\": 92\n",
            "      }\n",
            "    }\n",
            "  ]\n",
            "}\n"
          ]
        }
      ],
      "source": [
        "import { createReactAgent } from \"@langchain/langgraph/prebuilt\";\n",
        "\n",
        "const app = createReactAgent({\n",
        "  llm,\n",
        "  tools,\n",
        "});\n",
        "\n",
        "let agentOutput = await app.invoke({\n",
        "  messages: [\n",
        "    {\n",
        "      role: \"user\",\n",
        "      content: query\n",
        "    },\n",
        "  ],\n",
        "});\n",
        "\n",
        "console.log(agentOutput);"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "b0a390a2",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{\n",
              "  messages: [\n",
              "    HumanMessage {\n",
              "      \"id\": \"eeef343c-80d1-4ccb-86af-c109343689cd\",\n",
              "      \"content\": \"what is the value of magic_function(3)?\",\n",
              "      \"additional_kwargs\": {},\n",
              "      \"response_metadata\": {}\n",
              "    },\n",
              "    AIMessage {\n",
              "      \"id\": \"chatcmpl-A7exs2uRqEipaZ7MtRbXnqu0vT0Da\",\n",
              "      \"content\": \"\",\n",
              "      \"additional_kwargs\": {\n",
              "        \"tool_calls\": [\n",
              "          {\n",
              "            \"id\": \"call_MtwWLn000BQHeSYQKsbxYNR0\",\n",
              "            \"type\": \"function\",\n",
              "            \"function\": \"[Object]\"\n",
              "          }\n",
              "        ]\n",
              "      },\n",
              "      \"response_metadata\": {\n",
              "        \"tokenUsage\": {\n",
              "          \"completionTokens\": 14,\n",
              "          \"promptTokens\": 55,\n",
              "          \"totalTokens\": 69\n",
              "        },\n",
              "        \"finish_reason\": \"tool_calls\",\n",
              "        \"system_fingerprint\": \"fp_483d39d857\"\n",
              "      },\n",
              "      \"tool_calls\": [\n",
              "        {\n",
              "          \"name\": \"magic_function\",\n",
              "          \"args\": {\n",
              "            \"input\": 3\n",
              "          },\n",
              "          \"type\": \"tool_call\",\n",
              "          \"id\": \"call_MtwWLn000BQHeSYQKsbxYNR0\"\n",
              "        }\n",
              "      ],\n",
              "      \"invalid_tool_calls\": [],\n",
              "      \"usage_metadata\": {\n",
              "        \"input_tokens\": 55,\n",
              "        \"output_tokens\": 14,\n",
              "        \"total_tokens\": 69\n",
              "      }\n",
              "    },\n",
              "    ToolMessage {\n",
              "      \"id\": \"1001bf20-7cde-4f8b-81f1-1faa654a8bb4\",\n",
              "      \"content\": \"5\",\n",
              "      \"name\": \"magic_function\",\n",
              "      \"additional_kwargs\": {},\n",
              "      \"response_metadata\": {},\n",
              "      \"tool_call_id\": \"call_MtwWLn000BQHeSYQKsbxYNR0\"\n",
              "    },\n",
              "    AIMessage {\n",
              "      \"id\": \"chatcmpl-A7exsTk3ilzGzC8DuY8GpnKOaGdvx\",\n",
              "      \"content\": \"The value of `magic_function(3)` is 5.\",\n",
              "      \"additional_kwargs\": {},\n",
              "      \"response_metadata\": {\n",
              "        \"tokenUsage\": {\n",
              "          \"completionTokens\": 14,\n",
              "          \"promptTokens\": 78,\n",
              "          \"totalTokens\": 92\n",
              "        },\n",
              "        \"finish_reason\": \"stop\",\n",
              "        \"system_fingerprint\": \"fp_54e2f484be\"\n",
              "      },\n",
              "      \"tool_calls\": [],\n",
              "      \"invalid_tool_calls\": [],\n",
              "      \"usage_metadata\": {\n",
              "        \"input_tokens\": 78,\n",
              "        \"output_tokens\": 14,\n",
              "        \"total_tokens\": 92\n",
              "      }\n",
              "    },\n",
              "    HumanMessage {\n",
              "      \"id\": \"1f2a9f41-c8ff-48fe-9d93-e663ee9279ff\",\n",
              "      \"content\": \"Pardon?\",\n",
              "      \"additional_kwargs\": {},\n",
              "      \"response_metadata\": {}\n",
              "    },\n",
              "    AIMessage {\n",
              "      \"id\": \"chatcmpl-A7exyTe9Ofs63Ex3sKwRx3wWksNup\",\n",
              "      \"content\": \"The result of calling the `magic_function` with an input of 3 is 5.\",\n",
              "      \"additional_kwargs\": {},\n",
              "      \"response_metadata\": {\n",
              "        \"tokenUsage\": {\n",
              "          \"completionTokens\": 20,\n",
              "          \"promptTokens\": 102,\n",
              "          \"totalTokens\": 122\n",
              "        },\n",
              "        \"finish_reason\": \"stop\",\n",
              "        \"system_fingerprint\": \"fp_483d39d857\"\n",
              "      },\n",
              "      \"tool_calls\": [],\n",
              "      \"invalid_tool_calls\": [],\n",
              "      \"usage_metadata\": {\n",
              "        \"input_tokens\": 102,\n",
              "        \"output_tokens\": 20,\n",
              "        \"total_tokens\": 122\n",
              "      }\n",
              "    }\n",
              "  ]\n",
              "}"
            ]
          },
          "execution_count": 5,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "const messageHistory = agentOutput.messages;\n",
        "const newQuery = \"Pardon?\";\n",
        "\n",
        "agentOutput = await app.invoke({\n",
        "  messages: [\n",
        "    ...messageHistory,\n",
        "    { role: \"user\", content: newQuery }\n",
        "  ],\n",
        "});\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "41a12f7a",
      "metadata": {},
      "source": [
        "## Prompt Templates\n",
        "\n",
        "With legacy LangChain agents you have to pass in a prompt template. You can use\n",
        "this to control the agent.\n",
        "\n",
        "With LangGraph\n",
        "[react agent executor](https://langchain-ai.github.io/langgraphjs/reference/functions/prebuilt.createReactAgent.html),\n",
        "by default there is no prompt. You can achieve similar control over the agent in\n",
        "a few ways:\n",
        "\n",
        "1. Pass in a system message as input\n",
        "2. Initialize the agent with a system message\n",
        "3. Initialize the agent with a function to transform messages before passing to\n",
        "   the model.\n",
        "\n",
        "Let's take a look at all of these below. We will pass in custom instructions to\n",
        "get the agent to respond in Spanish.\n",
        "\n",
        "First up, using LangChain's `AgentExecutor`:\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "4c5266cc",
      "metadata": {
        "lines_to_next_cell": 2
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{\n",
              "  input: \u001b[32m\"what is the value of magic_function(3)?\"\u001b[39m,\n",
              "  output: \u001b[32m\"El valor de `magic_function(3)` es 5.\"\u001b[39m\n",
              "}"
            ]
          },
          "execution_count": 6,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "const spanishPrompt = ChatPromptTemplate.fromMessages([\n",
        "  [\"system\", \"You are a helpful assistant. Respond only in Spanish.\"],\n",
        "  [\"placeholder\", \"{chat_history}\"],\n",
        "  [\"human\", \"{input}\"],\n",
        "  [\"placeholder\", \"{agent_scratchpad}\"],\n",
        "]);\n",
        "\n",
        "const spanishAgent = createToolCallingAgent({\n",
        "  llm,\n",
        "  tools,\n",
        "  prompt: spanishPrompt,\n",
        "});\n",
        "const spanishAgentExecutor = new AgentExecutor({\n",
        "  agent: spanishAgent,\n",
        "  tools,\n",
        "});\n",
        "\n",
        "await spanishAgentExecutor.invoke({ input: query });\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c54b374d",
      "metadata": {},
      "source": [
        "Now, let's pass a custom system message to [react agent executor](https://langchain-ai.github.io/langgraphjs/reference/functions/prebuilt.createReactAgent.html).\n",
        "\n",
        "LangGraph's prebuilt `create_react_agent` does not take a prompt template directly as a parameter, but instead takes a `messages_modifier` parameter. This modifies messages before they are passed into the model, and can be one of four values:\n",
        "\n",
        "- A `SystemMessage`, which is added to the beginning of the list of messages.\n",
        "- A `string`, which is converted to a `SystemMessage` and added to the beginning of the list of messages.\n",
        "- A `Callable`, which should take in a list of messages. The output is then passed to the language model.\n",
        "- Or a [`Runnable`](/docs/concepts/lcel), which should should take in a list of messages. The output is then passed to the language model.\n",
        "\n",
        "Here's how it looks in action:\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "38a751ba",
      "metadata": {
        "lines_to_next_cell": 2
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "AIMessage {\n",
              "  \"id\": \"chatcmpl-A7ey8LGWAs8ldrRRcO5wlHM85w9T8\",\n",
              "  \"content\": \"El valor de `magic_function(3)` es 5.\",\n",
              "  \"additional_kwargs\": {},\n",
              "  \"response_metadata\": {\n",
              "    \"tokenUsage\": {\n",
              "      \"completionTokens\": 14,\n",
              "      \"promptTokens\": 89,\n",
              "      \"totalTokens\": 103\n",
              "    },\n",
              "    \"finish_reason\": \"stop\",\n",
              "    \"system_fingerprint\": \"fp_483d39d857\"\n",
              "  },\n",
              "  \"tool_calls\": [],\n",
              "  \"invalid_tool_calls\": [],\n",
              "  \"usage_metadata\": {\n",
              "    \"input_tokens\": 89,\n",
              "    \"output_tokens\": 14,\n",
              "    \"total_tokens\": 103\n",
              "  }\n",
              "}"
            ]
          },
          "execution_count": 7,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "const systemMessage = \"You are a helpful assistant. Respond only in Spanish.\";\n",
        "\n",
        "// This could also be a SystemMessage object\n",
        "// const systemMessage = new SystemMessage(\"You are a helpful assistant. Respond only in Spanish.\");\n",
        "\n",
        "const appWithSystemMessage = createReactAgent({\n",
        "  llm,\n",
        "  tools,\n",
        "  messageModifier: systemMessage,\n",
        "});\n",
        "\n",
        "agentOutput = await appWithSystemMessage.invoke({\n",
        "  messages: [\n",
        "    { role: \"user\", content: query }\n",
        "  ],\n",
        "});\n",
        "agentOutput.messages[agentOutput.messages.length - 1];"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7622d8f7",
      "metadata": {},
      "source": [
        "We can also pass in an arbitrary function. This function should take in a list\n",
        "of messages and output a list of messages. We can do all types of arbitrary\n",
        "formatting of messages here. In this cases, let's just add a `SystemMessage` to\n",
        "the start of the list of messages.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "c7120cdd",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "{\n",
            "  input: \"what is the value of magic_function(3)?\",\n",
            "  output: \"El valor de magic_function(3) es 5. ¡Pandemonium!\"\n",
            "}\n"
          ]
        }
      ],
      "source": [
        "import { BaseMessage, SystemMessage, HumanMessage } from \"@langchain/core/messages\";\n",
        "\n",
        "const modifyMessages = (messages: BaseMessage[]) => {\n",
        "  return [\n",
        "    new SystemMessage(\"You are a helpful assistant. Respond only in Spanish.\"),\n",
        "    ...messages,\n",
        "    new HumanMessage(\"Also say 'Pandemonium!' after the answer.\"),\n",
        "  ];\n",
        "};\n",
        "\n",
        "const appWithMessagesModifier = createReactAgent({\n",
        "  llm,\n",
        "  tools,\n",
        "  messageModifier: modifyMessages,\n",
        "});\n",
        "\n",
        "agentOutput = await appWithMessagesModifier.invoke({\n",
        "  messages: [{ role: \"user\", content: query }],\n",
        "});\n",
        "\n",
        "console.log({\n",
        "  input: query,\n",
        "  output: agentOutput.messages[agentOutput.messages.length - 1].content,\n",
        "});"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "44337a14",
      "metadata": {},
      "source": [
        "## Memory\n",
        "\n",
        "With LangChain's\n",
        "[`AgentExecutor`](https://api.js.langchain.com/classes/langchain_agents.AgentExecutor.html), you could add chat memory classes so it can engage in a multi-turn conversation.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "4d67ba36",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "The output of the magic function for the input 3 is 5.\n",
            "---\n",
            "Yes, your name is Polly! How can I assist you today?\n",
            "---\n",
            "The output of the magic function for the input 3 is 5.\n"
          ]
        }
      ],
      "source": [
        "import { ChatMessageHistory } from \"@langchain/community/stores/message/in_memory\";\n",
        "import { RunnableWithMessageHistory } from \"@langchain/core/runnables\";\n",
        "\n",
        "const memory = new ChatMessageHistory();\n",
        "const agentExecutorWithMemory = new RunnableWithMessageHistory({\n",
        "  runnable: agentExecutor,\n",
        "  getMessageHistory: () => memory,\n",
        "  inputMessagesKey: \"input\",\n",
        "  historyMessagesKey: \"chat_history\",\n",
        "});\n",
        "\n",
        "const config = { configurable: { sessionId: \"test-session\" } };\n",
        "\n",
        "agentOutput = await agentExecutorWithMemory.invoke(\n",
        "  { input: \"Hi, I'm polly! What's the output of magic_function of 3?\" },\n",
        "  config,\n",
        ");\n",
        "\n",
        "console.log(agentOutput.output);\n",
        "\n",
        "agentOutput = await agentExecutorWithMemory.invoke(\n",
        "  { input: \"Remember my name?\" },\n",
        "  config,\n",
        ");\n",
        "\n",
        "console.log(\"---\");\n",
        "console.log(agentOutput.output);\n",
        "console.log(\"---\");\n",
        "\n",
        "agentOutput = await agentExecutorWithMemory.invoke(\n",
        "  { input: \"what was that output again?\" },\n",
        "  config,\n",
        ");\n",
        "\n",
        "console.log(agentOutput.output);"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a7fe4e21",
      "metadata": {},
      "source": [
        "#### In LangGraph\n",
        "\n",
        "The equivalent to this type of memory in LangGraph is [persistence](https://langchain-ai.github.io/langgraphjs/how-tos/persistence/), and [checkpointing](https://langchain-ai.github.io/langgraphjs/reference/interfaces/index.Checkpoint.html).\n",
        "\n",
        "Add a `checkpointer` to the agent and you get chat memory for free. You'll need to also pass a `thread_id` within the `configurable` field in the `config` parameter. Notice that we only pass one message into each request, but the model still has context from previous runs:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "bbc64438",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Hi Polly! The output of the magic function for the input 3 is 5.\n",
            "---\n",
            "Yes, your name is Polly!\n",
            "---\n",
            "The output of the magic function for the input 3 was 5.\n"
          ]
        }
      ],
      "source": [
        "import { MemorySaver } from \"@langchain/langgraph\";\n",
        "\n",
        "const checkpointer = new MemorySaver();\n",
        "const appWithMemory = createReactAgent({\n",
        "  llm: llm,\n",
        "  tools: tools,\n",
        "  checkpointSaver: checkpointer\n",
        "});\n",
        "\n",
        "const langGraphConfig = {\n",
        "  configurable: {\n",
        "    thread_id: \"test-thread\",\n",
        "  },\n",
        "};\n",
        "\n",
        "agentOutput = await appWithMemory.invoke(\n",
        "  {\n",
        "    messages: [\n",
        "      {\n",
        "        role: \"user\",\n",
        "        content: \"Hi, I'm polly! What's the output of magic_function of 3?\",\n",
        "      }\n",
        "    ],\n",
        "  },\n",
        "  langGraphConfig,\n",
        ");\n",
        "\n",
        "console.log(agentOutput.messages[agentOutput.messages.length - 1].content);\n",
        "console.log(\"---\");\n",
        "\n",
        "agentOutput = await appWithMemory.invoke(\n",
        "  {\n",
        "    messages: [\n",
        "      { role: \"user\", content: \"Remember my name?\" }\n",
        "    ]\n",
        "  },\n",
        "  langGraphConfig,\n",
        ");\n",
        "\n",
        "console.log(agentOutput.messages[agentOutput.messages.length - 1].content);\n",
        "console.log(\"---\");\n",
        "\n",
        "agentOutput = await appWithMemory.invoke(\n",
        "  {\n",
        "    messages: [\n",
        "      { role: \"user\", content: \"what was that output again?\" }\n",
        "    ]\n",
        "  },\n",
        "  langGraphConfig,\n",
        ");\n",
        "\n",
        "console.log(agentOutput.messages[agentOutput.messages.length - 1].content);"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2997b4da",
      "metadata": {},
      "source": [
        "## Iterating through steps\n",
        "\n",
        "With LangChain's\n",
        "[`AgentExecutor`](https://api.js.langchain.com/classes/langchain_agents.AgentExecutor.html),\n",
        "you could iterate over the steps using the\n",
        "[`stream`](https://api.js.langchain.com/classes/langchain_core.runnables.Runnable.html#stream) method:\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "5c928049",
      "metadata": {
        "lines_to_next_cell": 2
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "{\n",
            "  intermediateSteps: [\n",
            "    {\n",
            "      action: {\n",
            "        tool: \"magic_function\",\n",
            "        toolInput: { input: 3 },\n",
            "        toolCallId: \"call_IQZr1yy2Ug6904VkQg6pWGgR\",\n",
            "        log: 'Invoking \"magic_function\" with {\"input\":3}\\n',\n",
            "        messageLog: [\n",
            "          AIMessageChunk {\n",
            "            \"id\": \"chatcmpl-A7eziUrDmLSSMoiOskhrfbsHqx4Sd\",\n",
            "            \"content\": \"\",\n",
            "            \"additional_kwargs\": {\n",
            "              \"tool_calls\": [\n",
            "                {\n",
            "                  \"index\": 0,\n",
            "                  \"id\": \"call_IQZr1yy2Ug6904VkQg6pWGgR\",\n",
            "                  \"type\": \"function\",\n",
            "                  \"function\": \"[Object]\"\n",
            "                }\n",
            "              ]\n",
            "            },\n",
            "            \"response_metadata\": {\n",
            "              \"prompt\": 0,\n",
            "              \"completion\": 0,\n",
            "              \"finish_reason\": \"tool_calls\",\n",
            "              \"system_fingerprint\": \"fp_483d39d857\"\n",
            "            },\n",
            "            \"tool_calls\": [\n",
            "              {\n",
            "                \"name\": \"magic_function\",\n",
            "                \"args\": {\n",
            "                  \"input\": 3\n",
            "                },\n",
            "                \"id\": \"call_IQZr1yy2Ug6904VkQg6pWGgR\",\n",
            "                \"type\": \"tool_call\"\n",
            "              }\n",
            "            ],\n",
            "            \"tool_call_chunks\": [\n",
            "              {\n",
            "                \"name\": \"magic_function\",\n",
            "                \"args\": \"{\\\"input\\\":3}\",\n",
            "                \"id\": \"call_IQZr1yy2Ug6904VkQg6pWGgR\",\n",
            "                \"index\": 0,\n",
            "                \"type\": \"tool_call_chunk\"\n",
            "              }\n",
            "            ],\n",
            "            \"invalid_tool_calls\": [],\n",
            "            \"usage_metadata\": {\n",
            "              \"input_tokens\": 61,\n",
            "              \"output_tokens\": 14,\n",
            "              \"total_tokens\": 75\n",
            "            }\n",
            "          }\n",
            "        ]\n",
            "      },\n",
            "      observation: \"5\"\n",
            "    }\n",
            "  ]\n",
            "}\n",
            "{ output: \"The value of `magic_function(3)` is 5.\" }\n"
          ]
        }
      ],
      "source": [
        "const langChainStream = await agentExecutor.stream({ input: query });\n",
        "\n",
        "for await (const step of langChainStream) {\n",
        "  console.log(step);\n",
        "}"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "cd371818",
      "metadata": {},
      "source": [
        "#### In LangGraph\n",
        "\n",
        "In LangGraph, things are handled natively using the stream method.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "2be89a30",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "{\n",
            "  agent: {\n",
            "    messages: [\n",
            "      AIMessage {\n",
            "        \"id\": \"chatcmpl-A7ezu8hirCENjdjR2GpLjkzXFTEmp\",\n",
            "        \"content\": \"\",\n",
            "        \"additional_kwargs\": {\n",
            "          \"tool_calls\": [\n",
            "            {\n",
            "              \"id\": \"call_KhhNL0m3mlPoJiboFMoX8hzk\",\n",
            "              \"type\": \"function\",\n",
            "              \"function\": \"[Object]\"\n",
            "            }\n",
            "          ]\n",
            "        },\n",
            "        \"response_metadata\": {\n",
            "          \"tokenUsage\": {\n",
            "            \"completionTokens\": 14,\n",
            "            \"promptTokens\": 55,\n",
            "            \"totalTokens\": 69\n",
            "          },\n",
            "          \"finish_reason\": \"tool_calls\",\n",
            "          \"system_fingerprint\": \"fp_483d39d857\"\n",
            "        },\n",
            "        \"tool_calls\": [\n",
            "          {\n",
            "            \"name\": \"magic_function\",\n",
            "            \"args\": {\n",
            "              \"input\": 3\n",
            "            },\n",
            "            \"type\": \"tool_call\",\n",
            "            \"id\": \"call_KhhNL0m3mlPoJiboFMoX8hzk\"\n",
            "          }\n",
            "        ],\n",
            "        \"invalid_tool_calls\": [],\n",
            "        \"usage_metadata\": {\n",
            "          \"input_tokens\": 55,\n",
            "          \"output_tokens\": 14,\n",
            "          \"total_tokens\": 69\n",
            "        }\n",
            "      }\n",
            "    ]\n",
            "  }\n",
            "}\n",
            "{\n",
            "  tools: {\n",
            "    messages: [\n",
            "      ToolMessage {\n",
            "        \"content\": \"5\",\n",
            "        \"name\": \"magic_function\",\n",
            "        \"additional_kwargs\": {},\n",
            "        \"response_metadata\": {},\n",
            "        \"tool_call_id\": \"call_KhhNL0m3mlPoJiboFMoX8hzk\"\n",
            "      }\n",
            "    ]\n",
            "  }\n",
            "}\n",
            "{\n",
            "  agent: {\n",
            "    messages: [\n",
            "      AIMessage {\n",
            "        \"id\": \"chatcmpl-A7ezuTrh8GC550eKa1ZqRZGjpY5zh\",\n",
            "        \"content\": \"The value of `magic_function(3)` is 5.\",\n",
            "        \"additional_kwargs\": {},\n",
            "        \"response_metadata\": {\n",
            "          \"tokenUsage\": {\n",
            "            \"completionTokens\": 14,\n",
            "            \"promptTokens\": 78,\n",
            "            \"totalTokens\": 92\n",
            "          },\n",
            "          \"finish_reason\": \"stop\",\n",
            "          \"system_fingerprint\": \"fp_483d39d857\"\n",
            "        },\n",
            "        \"tool_calls\": [],\n",
            "        \"invalid_tool_calls\": [],\n",
            "        \"usage_metadata\": {\n",
            "          \"input_tokens\": 78,\n",
            "          \"output_tokens\": 14,\n",
            "          \"total_tokens\": 92\n",
            "        }\n",
            "      }\n",
            "    ]\n",
            "  }\n",
            "}\n"
          ]
        }
      ],
      "source": [
        "const langGraphStream = await app.stream(\n",
        "  { messages: [{ role: \"user\", content: query }] },\n",
        "  { streamMode: \"updates\" },\n",
        ");\n",
        "\n",
        "for await (const step of langGraphStream) {\n",
        "  console.log(step);\n",
        "}"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ce023792",
      "metadata": {},
      "source": [
        "## `returnIntermediateSteps`\n",
        "\n",
        "Setting this parameter on AgentExecutor allows users to access\n",
        "intermediate_steps, which pairs agent actions (e.g., tool invocations) with\n",
        "their outcomes."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "77ce2771",
      "metadata": {
        "lines_to_next_cell": 2
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[\n",
            "  {\n",
            "    action: {\n",
            "      tool: \"magic_function\",\n",
            "      toolInput: { input: 3 },\n",
            "      toolCallId: \"call_mbg1xgLEYEEWClbEaDe7p5tK\",\n",
            "      log: 'Invoking \"magic_function\" with {\"input\":3}\\n',\n",
            "      messageLog: [\n",
            "        AIMessageChunk {\n",
            "          \"id\": \"chatcmpl-A7f0NdSRSUJsBP6ENTpiQD4LzpBAH\",\n",
            "          \"content\": \"\",\n",
            "          \"additional_kwargs\": {\n",
            "            \"tool_calls\": [\n",
            "              {\n",
            "                \"index\": 0,\n",
            "                \"id\": \"call_mbg1xgLEYEEWClbEaDe7p5tK\",\n",
            "                \"type\": \"function\",\n",
            "                \"function\": \"[Object]\"\n",
            "              }\n",
            "            ]\n",
            "          },\n",
            "          \"response_metadata\": {\n",
            "            \"prompt\": 0,\n",
            "            \"completion\": 0,\n",
            "            \"finish_reason\": \"tool_calls\",\n",
            "            \"system_fingerprint\": \"fp_54e2f484be\"\n",
            "          },\n",
            "          \"tool_calls\": [\n",
            "            {\n",
            "              \"name\": \"magic_function\",\n",
            "              \"args\": {\n",
            "                \"input\": 3\n",
            "              },\n",
            "              \"id\": \"call_mbg1xgLEYEEWClbEaDe7p5tK\",\n",
            "              \"type\": \"tool_call\"\n",
            "            }\n",
            "          ],\n",
            "          \"tool_call_chunks\": [\n",
            "            {\n",
            "              \"name\": \"magic_function\",\n",
            "              \"args\": \"{\\\"input\\\":3}\",\n",
            "              \"id\": \"call_mbg1xgLEYEEWClbEaDe7p5tK\",\n",
            "              \"index\": 0,\n",
            "              \"type\": \"tool_call_chunk\"\n",
            "            }\n",
            "          ],\n",
            "          \"invalid_tool_calls\": [],\n",
            "          \"usage_metadata\": {\n",
            "            \"input_tokens\": 61,\n",
            "            \"output_tokens\": 14,\n",
            "            \"total_tokens\": 75\n",
            "          }\n",
            "        }\n",
            "      ]\n",
            "    },\n",
            "    observation: \"5\"\n",
            "  }\n",
            "]\n"
          ]
        }
      ],
      "source": [
        "const agentExecutorWithIntermediateSteps = new AgentExecutor({\n",
        "  agent,\n",
        "  tools,\n",
        "  returnIntermediateSteps: true,\n",
        "});\n",
        "\n",
        "const result = await agentExecutorWithIntermediateSteps.invoke({\n",
        "  input: query,\n",
        "});\n",
        "\n",
        "console.log(result.intermediateSteps);\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "050845ae",
      "metadata": {},
      "source": [
        "By default the\n",
        "[react agent executor](https://langchain-ai.github.io/langgraphjs/reference/functions/prebuilt.createReactAgent.html)\n",
        "in LangGraph appends all messages to the central state. Therefore, it is easy to\n",
        "see any intermediate steps by just looking at the full state.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "2f9cdfa8",
      "metadata": {
        "lines_to_next_cell": 2
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[\n",
            "  HumanMessage {\n",
            "    \"id\": \"46a825b2-13a3-4f19-b1aa-7716c53eb247\",\n",
            "    \"content\": \"what is the value of magic_function(3)?\",\n",
            "    \"additional_kwargs\": {},\n",
            "    \"response_metadata\": {}\n",
            "  },\n",
            "  AIMessage {\n",
            "    \"id\": \"chatcmpl-A7f0iUuWktC8gXztWZCjofqyCozY2\",\n",
            "    \"content\": \"\",\n",
            "    \"additional_kwargs\": {\n",
            "      \"tool_calls\": [\n",
            "        {\n",
            "          \"id\": \"call_ndsPDU58wsMeGaqr41cSlLlF\",\n",
            "          \"type\": \"function\",\n",
            "          \"function\": \"[Object]\"\n",
            "        }\n",
            "      ]\n",
            "    },\n",
            "    \"response_metadata\": {\n",
            "      \"tokenUsage\": {\n",
            "        \"completionTokens\": 14,\n",
            "        \"promptTokens\": 55,\n",
            "        \"totalTokens\": 69\n",
            "      },\n",
            "      \"finish_reason\": \"tool_calls\",\n",
            "      \"system_fingerprint\": \"fp_483d39d857\"\n",
            "    },\n",
            "    \"tool_calls\": [\n",
            "      {\n",
            "        \"name\": \"magic_function\",\n",
            "        \"args\": {\n",
            "          \"input\": 3\n",
            "        },\n",
            "        \"type\": \"tool_call\",\n",
            "        \"id\": \"call_ndsPDU58wsMeGaqr41cSlLlF\"\n",
            "      }\n",
            "    ],\n",
            "    \"invalid_tool_calls\": [],\n",
            "    \"usage_metadata\": {\n",
            "      \"input_tokens\": 55,\n",
            "      \"output_tokens\": 14,\n",
            "      \"total_tokens\": 69\n",
            "    }\n",
            "  },\n",
            "  ToolMessage {\n",
            "    \"id\": \"ac6aa309-bbfb-46cd-ba27-cbdbfd848705\",\n",
            "    \"content\": \"5\",\n",
            "    \"name\": \"magic_function\",\n",
            "    \"additional_kwargs\": {},\n",
            "    \"response_metadata\": {},\n",
            "    \"tool_call_id\": \"call_ndsPDU58wsMeGaqr41cSlLlF\"\n",
            "  },\n",
            "  AIMessage {\n",
            "    \"id\": \"chatcmpl-A7f0i7iHyDUV6is6sgwtcXivmFZ1x\",\n",
            "    \"content\": \"The value of `magic_function(3)` is 5.\",\n",
            "    \"additional_kwargs\": {},\n",
            "    \"response_metadata\": {\n",
            "      \"tokenUsage\": {\n",
            "        \"completionTokens\": 14,\n",
            "        \"promptTokens\": 78,\n",
            "        \"totalTokens\": 92\n",
            "      },\n",
            "      \"finish_reason\": \"stop\",\n",
            "      \"system_fingerprint\": \"fp_54e2f484be\"\n",
            "    },\n",
            "    \"tool_calls\": [],\n",
            "    \"invalid_tool_calls\": [],\n",
            "    \"usage_metadata\": {\n",
            "      \"input_tokens\": 78,\n",
            "      \"output_tokens\": 14,\n",
            "      \"total_tokens\": 92\n",
            "    }\n",
            "  }\n",
            "]\n"
          ]
        }
      ],
      "source": [
        "agentOutput = await app.invoke({\n",
        "  messages: [\n",
        "    { role: \"user\", content: query },\n",
        "  ]\n",
        "});\n",
        "\n",
        "console.log(agentOutput.messages);"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f6e671e6",
      "metadata": {},
      "source": [
        "## `maxIterations`\n",
        "\n",
        "`AgentExecutor` implements a `maxIterations` parameter, whereas this is\n",
        "controlled via `recursionLimit` in LangGraph.\n",
        "\n",
        "Note that in the LangChain `AgentExecutor`, an \"iteration\" includes a full turn of tool\n",
        "invocation and execution. In LangGraph, each step contributes to the recursion\n",
        "limit, so we will need to multiply by two (and add one) to get equivalent\n",
        "results.\n",
        "\n",
        "Here's an example of how you'd set this parameter with the legacy `AgentExecutor`:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "1cca9d11",
      "metadata": {
        "lines_to_next_cell": 2
      },
      "outputs": [],
      "source": [
        "const badMagicTool = tool(async ({ input: _input }) => {\n",
        "  return \"Sorry, there was a temporary error. Please try again with the same input.\";\n",
        "}, {\n",
        "  name: \"magic_function\",\n",
        "  description: \"Applies a magic function to an input.\",\n",
        "  schema: z.object({\n",
        "    input: z.string(),\n",
        "  }),\n",
        "});\n",
        "\n",
        "const badTools = [badMagicTool];\n",
        "\n",
        "const spanishAgentExecutorWithMaxIterations = new AgentExecutor({\n",
        "  agent: createToolCallingAgent({\n",
        "    llm,\n",
        "    tools: badTools,\n",
        "    prompt: spanishPrompt,\n",
        "  }),\n",
        "  tools: badTools,\n",
        "  verbose: true,\n",
        "  maxIterations: 2,\n",
        "});\n",
        "\n",
        "await spanishAgentExecutorWithMaxIterations.invoke({ input: query });"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "245e064c",
      "metadata": {},
      "source": [
        "If the recursion limit is reached in LangGraph.js, the framework will raise a specific exception type that we can catch and manage similarly to AgentExecutor."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "2f5e7d58",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Recursion limit reached.\n"
          ]
        }
      ],
      "source": [
        "import { GraphRecursionError } from \"@langchain/langgraph\";\n",
        "\n",
        "const RECURSION_LIMIT = 2 * 2 + 1;\n",
        "\n",
        "const appWithBadTools = createReactAgent({ llm, tools: badTools });\n",
        "\n",
        "try {\n",
        "  await appWithBadTools.invoke({\n",
        "    messages: [\n",
        "      { role: \"user\", content: query }\n",
        "    ]\n",
        "  }, {\n",
        "    recursionLimit: RECURSION_LIMIT,\n",
        "  });\n",
        "} catch (e) {\n",
        "  if (e instanceof GraphRecursionError) {\n",
        "    console.log(\"Recursion limit reached.\");\n",
        "  } else {\n",
        "    throw e;\n",
        "  }\n",
        "}"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e56203e7",
      "metadata": {},
      "source": [
        "## Next steps\n",
        "\n",
        "You've now learned how to migrate your LangChain agent executors to LangGraph.\n",
        "\n",
        "Next, check out other [LangGraph how-to guides](https://langchain-ai.github.io/langgraphjs/how-tos/)."
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Deno",
      "language": "typescript",
      "name": "deno"
    },
    "language_info": {
      "file_extension": ".ts",
      "mimetype": "text/x.typescript",
      "name": "typescript",
      "nb_converter": "script",
      "pygments_lexer": "typescript",
      "version": "5.3.3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}
